package ar.com.arkios.kfcajitas;

import ar.com.arkios.kfcajitas.exception.InitializationSocketException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Observable;
import java.util.Observer;

/**
 * <p>
 * Representa una caja con dispositivos dentro. Cada dispositivo emite eventos
 * o puede esperar eventos (ej: escaner, display respectivamente).
 * </p>
 * 
 * <p>
 * La idea sería obtener la metadata de los dispositivos alguna fuente (4D o XML)
 * y a partir de la misma, instanciar los dispositivos asociados a la caja.
 * </p>
 * 
 * <p>
 * La caja siempre está asociada a una línea de producción. Esta última podrá
 * tener o no asociado un plan de producción. Por ende, la caja implementa la
 * interfaz Observer para poder ser actualizada en caso de que la LineaProduccion
 * a la que está asociada cambie el Plan de Producción o se pare. Es decir, la
 * Linea Produccion deberá notificar a sus observadores (las cajas asosicadas a
 * la misma) cualquier cambio que ocurra sobre la misma.
 * </p>
 * 
 * <p>
 * Al mismo tiempo, la caja observa los distintos dispositivos que tiene asociados
 * (de ser necesario). Existen dispositivos que envían eventos los cuales deberán
 * ser atendidos por la caja. En estos casos, la caja deberá observar los dispositivos
 * que emitan eventos esporádicos para atender los mismo y realizar la lógica 
 * necesaria (prender luces, mostrar carteles en el display o cosas similares).
 * </p>
 * 
 * <p>
 * En principio la caja solo debe atender los eventos de los dispositivos y operar
 * en base a los mismos. La caja es la encargada de instanciar los dispositivos
 * asociados a la misma. Por el momento, esto implica que la caja crea el socket
 * que utiliza cada dispositivo para comunicarse. Esto puede llegar a ser 
 * responsabilidad del dispositivo.
 * </p>
 */
public class Caja extends Thread implements Observer {

    /** 
     * Dirección IP de la caja. Los dispositivos pueden tener sus puertos o
     * la caja puede saber en que puertos está cada dispositivo. Por el momento
     * la caja es la que sabe en que puerto está cada dispositivo. CAMBIAR SI ES
     * NECESARIO.
     */
    private String miIp;
    
    /** Línea de Producción a la que está asociada una caja. Esto no debería ser NUNCA NULL */
    private LineaProduccion miLineaProduccion;
    
    /** Socket en el que se reciben los mensajes de la caja física */
    private ServerSocket miServerSocket;
    private static final int TIMEOUT = 2000;
    
    /** 
     * Dispositivos asociados a la caja.
     */
    private Escaner miEscaner;
    private Display miDisplay;
    
    private boolean salir;
    
    
    public Caja(LineaProduccion laLineaProduccion, String laIp) {
        this.miIp = laIp;
        this.miLineaProduccion = laLineaProduccion;
        this.salir = false;
        
        try {
            this.miServerSocket = new ServerSocket();
            this.miServerSocket.setSoTimeout(TIMEOUT);
        } catch (IOException e) {
            throw new InitializationSocketException("No se pudo crear el socket para la caja", e);
        }
        
        // TODO: comunicarse con el dispositivo del scanner e indicarle el puerto 
        // e ip al que se tiene que conectar.
    }

    @Override
    public void run() {
        Socket socketCliente = null;
        while(!salir) {
            try {
                socketCliente = this.miServerSocket.accept();
            } catch (IOException e) {
                // TODO: hacer algo un poco mas limpio en caso de tener una excepción aca.
                throw new RuntimeException(e);
            }
            
            if (socketCliente != null) {
                // Supongo que el escaner y el display están conectados al mismo puerto dodne
                // uno escucha y otro envía datos.
                this.miEscaner = new Escaner(socketCliente, null); // POR AHORA SE LE PASA PROTOCOLO NULL DADO QUE NO ESTA DEFINIDO!!!
                this.miDisplay = new Display(socketCliente);
                this.miEscaner.addObserver(this);
                startUp();
            }
        }
    }

    public void update(Observable o, Object arg) {
        // Ver como no hacer para tener un instanceOf aca dado que la Caja es observer
        // de dos objetos, la linea de producción y el escaner. NO ESTA BUENO HACER
        // INSTANCEOF.
        if (o instanceof LineaProduccion) {
            LineaProduccion lineaProduccion = (LineaProduccion) o;
            
            if (lineaProduccion.tienePlanProduccion()) {
                this.miDisplay.sendMensaje("Cambio en el plan de produccion");
                // Ver que mas hacer aca.
            } else {
                this.miDisplay.sendMensaje("No hay un proceso activo para la linea");
                // Ver que mas hacer aca.
            }
        } else if (o instanceof Escaner) {
            
        }
    }

    /**
     * Realiza toda la secuencia de mensajes para indicar que la caja esta 
     * conectada con el servidor.
     */
    private void startUp() {
        
    }
    
}
